﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;
using Bronze.ReportBuilder.Toolbox.Controls;
using System.Collections;
using Gizmox.WebGUI.Forms;
using System.ComponentModel;

namespace Bronze.ReportBuilder
{
    public class Utils
    {
        static Dictionary<Type, IToolboxItem> toolboxItems;
        public static Dictionary<Type, IToolboxItem> GetToolboxs()
        {
            if (toolboxItems == null)
            {
                toolboxItems = new Dictionary<Type, IToolboxItem>();

                foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                {
                    var types = assembly.GetTypes();
                    foreach (var type in types)
                    {
                        if (!type.IsAbstract && !type.IsInterface && typeof(IToolboxItem).IsAssignableFrom(type))
                        {
                            var item = (IToolboxItem)Activator.CreateInstance(type);
                            toolboxItems[item.ControlType] = item;
                        }
                    }
                }
            }
            return toolboxItems;
        }

        public static IToolboxItem GetToolbox(Type t)
        {
            var items = GetToolboxs();
            if (items.ContainsKey(t))
            {
                return items[t];
            }
            return null;
        }


        public static Element ToElement(FormDefinition form, Gizmox.WebGUI.Forms.Component component, Dictionary<Gizmox.WebGUI.Forms.Component, List<string>> trackedProperties)
        {
            var type=component.GetType();
            var typeIndex = form.GetTypeIndex(type.AssemblyQualifiedName);
            

            var el = new Element {  TypeIndex=typeIndex
                //Type = type.AssemblyQualifiedName 
            };
            var elProperties = new List<NameValue>();

            if (trackedProperties.ContainsKey(component))
            {
                var includeProperties = trackedProperties[component];
                el.IsTrack = true;
                foreach (var prop in includeProperties)
                {
                    var nameVaule = new NameValue { Name = prop };
                    var value = ReflectionHelper.GetProperty(component, prop);
                    nameVaule.Value = value;

                    if (value is Gizmox.WebGUI.Forms.Component)
                    {
                        continue;
                    }
                    else if (value is IList && value is ICollection)
                    {
                        ElementList els = new ElementList();

                        typeIndex = form.GetTypeIndex(value.GetType().AssemblyQualifiedName);
                        els.TypeIndex = typeIndex;


                        var collection = (IList)value;
                        foreach (Gizmox.WebGUI.Forms.Component item in collection)
                        {
                            var child = ToElement(form,item, trackedProperties);
                            els.Elements.Add(child);
                        }
                        nameVaule.Value = els;
                    }
                    elProperties.Add(nameVaule);
                }
            }
            else
            {
                try
                {
                    var name = ReflectionHelper.GetProperty(component, "Name");
                    if (name != null)
                    {
                        elProperties.Add(new NameValue { Name = "Name", Value = name });
                    }
                }
                catch (Exception)
                {
                }

                var propNames = ReflectionHelper.GetProperties(component);

                foreach (PropertyDescriptor prop in propNames)
                {
                    if (prop.PropertyType.IsArray && prop.IsReadOnly) continue;
                    if (prop.Name == "DataBindings") continue;

                    if (prop.Attributes.Contains(DesignerSerializationVisibilityAttribute.Hidden))
                        continue;
                    
                    var value = prop.GetValue(component);

                    var defaultValue = prop.GetDefaultValue();
                    if (object.Equals(value, defaultValue))
                        continue;


                    if (prop.PropertyType==typeof(string))
                    {
                        if (value.IsNull() && defaultValue.IsNull())
                            continue;
                    }

                    var nameVaule = new NameValue { Name = prop.Name };
                    nameVaule.Value = value;

                    if (value is Gizmox.WebGUI.Forms.Component)
                    {
                        continue;
                        //nameVaule.Value = ToElement((Gizmox.WebGUI.Forms.Component)value);
                    }
                    else if (value is IList && value is ICollection)
                    {
                        ElementList els = new ElementList();
                        typeIndex = form.GetTypeIndex(value.GetType().AssemblyQualifiedName);
                        els.TypeIndex = typeIndex;
                        var collection = (IList)value;
                        foreach (Gizmox.WebGUI.Forms.Component item in collection)
                        {
                            var child = ToElement(form,item, trackedProperties);
                            els.Elements.Add(child);
                        }
                        nameVaule.Value = els;
                    }
                    elProperties.Add(nameVaule);
                }
            }
            el.Properties = elProperties;
            return el;
        }

        public static Gizmox.WebGUI.Forms.Component ToControl(FormDefinition form, Element el, ref Dictionary<Gizmox.WebGUI.Forms.Component, List<string>> trackedProperties)
        {
            var type = form.GetTypeName(el.TypeIndex);
            var control = (Gizmox.WebGUI.Forms.Component)Activator.CreateInstance(type);

            var trackPrpos = new List<string>();

            foreach (var prop in el.Properties)
            {
                if (el.IsTrack)
                {
                    trackPrpos.Add(prop.Name);
                }

                var value = prop.Value;
                if (value is ElementList)
                {
                    var elements = value as ElementList;
                    IList cpList = null;

                    var cpListType = form.GetTypeName(elements.TypeIndex);
                    if (cpListType.IsArray)
                    {
                        var array = Array.CreateInstance(cpListType.GetElementType(), elements.Elements.Count);
                        for (int i = 0; i < elements.Elements.Count; i++)
                        {
                            var item = elements.Elements[i];
                            var cpt = ToControl(form,item, ref trackedProperties);
                            array.SetValue(cpt, i);
                        }
                        ReflectionHelper.SetProperty(control, prop.Name, array);
                    }
                    else
                    {
                        var controlItems = ReflectionHelper.GetProperty(control, prop.Name);
                        if (controlItems != null)
                        {
                            cpList = (IList)controlItems;
                        }
                        else
                        {
                            cpList = (IList)Activator.CreateInstance(cpListType);
                            ReflectionHelper.SetProperty(control, prop.Name, cpList);
                        }

                        foreach (var item in elements.Elements)
                        {
                            var cpt = ToControl(form,item, ref trackedProperties);
                            cpList.Add(cpt);
                        }
                    }
                }
                else
                {
                    ReflectionHelper.SetProperty(control, prop.Name, prop.Value);
                }
            }
            if (el.IsTrack && trackedProperties != null)
            {
                trackedProperties[control] = trackPrpos;
            }
            return control;
        }
    }


    public static class ReflectionHelper
    {
        public static object GetProperty(object source, string propertyName)
        {
            PropertyInfo propertyInfo = source.GetType().GetProperty(propertyName);
            if (propertyInfo == null)
                return null;

            return propertyInfo.GetValue(source, null);
        }

        public static void SetProperty(object source, string propertyName, object value)
        {
            PropertyInfo propertyInfo = source.GetType().GetProperty(propertyName);
            if (propertyInfo == null)
                return;
            if (propertyInfo.CanWrite)
            {
                propertyInfo.SetValue(source, value, null);
            }
        }

        public static PropertyDescriptorCollection GetProperties(Gizmox.WebGUI.Forms.Component component)
        {
            return TypeDescriptor.GetProperties(component, new Attribute[] { new BrowsableAttribute(true) });
        }

    }



}